"""
兜底机制管理器 - 关键路径的兜底保障

这个模块实现了兜底机制管理：
- 多级兜底策略
- 关键路径保护
- 自动兜底触发
- 兜底恢复机制
- 用户体验保障
"""

import asyncio
import logging
import time
from dataclasses import dataclass, field
from datetime import datetime, timezone, timedelta
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Union, Tuple
import json
import hashlib

from circuit_breaker import CircuitBreaker, CircuitBreakerConfig
from degradation_manager import DegradationLevel, get_degradation_manager

logger = logging.getLogger(__name__)


class FallbackType(Enum):
    """兜底类型"""
    CACHE = "cache"              # 缓存兜底
    STATIC = "static"            # 静态内容兜底
    SIMPLIFIED = "simplified"    # 简化功能兜底
    QUEUE = "queue"              # 队列兜底
    RETRY = "retry"              # 重试兜底
    CUSTOM = "custom"            # 自定义兜底


class FallbackPriority(Enum):
    """兜底优先级"""
    CRITICAL = 1    # 关键路径
    HIGH = 2        # 高优先级
    MEDIUM = 3      # 中等优先级
    LOW = 4         # 低优先级


@dataclass
class FallbackStrategy:
    """兜底策略"""
    name: str
    service_name: str
    fallback_type: FallbackType
    priority: FallbackPriority
    trigger_conditions: List[str]  # 触发条件
    fallback_func: Optional[Callable] = None
    cache_ttl: int = 300  # 缓存TTL（秒）
    max_retries: int = 3  # 最大重试次数
    retry_delay: float = 1.0  # 重试延迟（秒）
    enabled: bool = True
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class FallbackResult:
    """兜底结果"""
    success: bool
    data: Any
    source: str  # "primary", "fallback", "cache"
    fallback_type: Optional[FallbackType] = None
    execution_time: float = 0.0
    error_message: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class FallbackConfig:
    """兜底配置"""
    # 缓存配置
    enable_cache_fallback: bool = True
    cache_size_limit: int = 1000
    cache_ttl_default: int = 300
    
    # 重试配置
    enable_retry_fallback: bool = True
    max_retry_attempts: int = 3
    retry_delay_base: float = 1.0
    retry_delay_max: float = 30.0
    
    # 队列配置
    enable_queue_fallback: bool = True
    queue_size_limit: int = 10000
    queue_processing_interval: int = 60
    
    # 监控配置
    enable_fallback_monitoring: bool = True
    fallback_metrics_retention: int = 86400  # 24小时
    
    # 全局配置
    enable_auto_fallback: bool = True
    fallback_timeout: float = 5.0
    max_concurrent_fallbacks: int = 10


class FallbackManager:
    """兜底机制管理器"""
    
    def __init__(self, config: Optional[FallbackConfig] = None):
        self.config = config or FallbackConfig()
        
        # 兜底策略管理
        self.fallback_strategies: Dict[str, List[FallbackStrategy]] = {}
        self.active_fallbacks: Dict[str, FallbackStrategy] = {}
        
        # 缓存管理
        self.fallback_cache: Dict[str, Tuple[Any, float]] = {}  # key -> (data, expiry_time)
        
        # 队列管理
        self.fallback_queues: Dict[str, List[Dict[str, Any]]] = {}
        
        # 统计信息
        self.fallback_stats: Dict[str, Dict[str, int]] = {}
        
        # 运行状态
        self.is_running = False
        self.queue_processor_task: Optional[asyncio.Task] = None
        self.cache_cleanup_task: Optional[asyncio.Task] = None
        
        # 锁
        self.lock = asyncio.Lock()
        self.cache_lock = asyncio.Lock()
        self.queue_lock = asyncio.Lock()
        
        self.logger = logging.getLogger(__name__)
        self.logger.info("兜底机制管理器已初始化")
    
    async def start(self) -> None:
        """启动兜底管理器"""
        if self.is_running:
            return
        
        self.is_running = True
        
        # 启动队列处理任务
        if self.config.enable_queue_fallback:
            self.queue_processor_task = asyncio.create_task(self._queue_processor_worker())
        
        # 启动缓存清理任务
        if self.config.enable_cache_fallback:
            self.cache_cleanup_task = asyncio.create_task(self._cache_cleanup_worker())
        
        self.logger.info("兜底机制管理器已启动")
    
    async def stop(self) -> None:
        """停止兜底管理器"""
        if not self.is_running:
            return
        
        self.is_running = False
        
        # 停止任务
        if self.queue_processor_task:
            self.queue_processor_task.cancel()
            try:
                await self.queue_processor_task
            except asyncio.CancelledError:
                pass
        
        if self.cache_cleanup_task:
            self.cache_cleanup_task.cancel()
            try:
                await self.cache_cleanup_task
            except asyncio.CancelledError:
                pass
        
        self.logger.info("兜底机制管理器已停止")
    
    async def register_fallback_strategy(self, strategy: FallbackStrategy) -> None:
        """注册兜底策略"""
        async with self.lock:
            service_name = strategy.service_name
            if service_name not in self.fallback_strategies:
                self.fallback_strategies[service_name] = []
            
            self.fallback_strategies[service_name].append(strategy)
            
            # 按优先级排序
            self.fallback_strategies[service_name].sort(key=lambda s: s.priority.value)
            
            # 初始化统计信息
            if service_name not in self.fallback_stats:
                self.fallback_stats[service_name] = {
                    "total_requests": 0,
                    "primary_success": 0,
                    "fallback_success": 0,
                    "fallback_failed": 0,
                    "cache_hits": 0
                }
            
            self.logger.info(f"已注册兜底策略: {strategy.name} for {service_name}")
    
    async def execute_with_fallback(self, 
                                   service_name: str,
                                   primary_func: Callable,
                                   *args,
                                   **kwargs) -> FallbackResult:
        """执行带兜底的函数调用"""
        start_time = time.time()
        
        # 更新统计信息
        if service_name in self.fallback_stats:
            self.fallback_stats[service_name]["total_requests"] += 1
        
        try:
            # 尝试执行主要功能
            if asyncio.iscoroutinefunction(primary_func):
                result = await asyncio.wait_for(
                    primary_func(*args, **kwargs),
                    timeout=self.config.fallback_timeout
                )
            else:
                result = await asyncio.wait_for(
                    asyncio.get_event_loop().run_in_executor(None, primary_func, *args, **kwargs),
                    timeout=self.config.fallback_timeout
                )
            
            execution_time = time.time() - start_time
            
            # 主要功能成功
            if service_name in self.fallback_stats:
                self.fallback_stats[service_name]["primary_success"] += 1
            
            return FallbackResult(
                success=True,
                data=result,
                source="primary",
                execution_time=execution_time
            )
            
        except Exception as e:
            # 主要功能失败，尝试兜底
            self.logger.warning(f"主要功能失败，尝试兜底: {service_name}, 错误: {e}")
            
            fallback_result = await self._execute_fallback(
                service_name, primary_func, *args, **kwargs
            )
            
            if fallback_result.success:
                if service_name in self.fallback_stats:
                    self.fallback_stats[service_name]["fallback_success"] += 1
            else:
                if service_name in self.fallback_stats:
                    self.fallback_stats[service_name]["fallback_failed"] += 1
            
            return fallback_result
    
    async def _execute_fallback(self, 
                               service_name: str,
                               primary_func: Callable,
                               *args,
                               **kwargs) -> FallbackResult:
        """执行兜底逻辑"""
        if service_name not in self.fallback_strategies:
            return FallbackResult(
                success=False,
                data=None,
                source="fallback",
                error_message="No fallback strategies available"
            )
        
        strategies = self.fallback_strategies[service_name]
        
        for strategy in strategies:
            if not strategy.enabled:
                continue
            
            try:
                result = await self._try_fallback_strategy(strategy, primary_func, *args, **kwargs)
                if result.success:
                    return result
            except Exception as e:
                self.logger.error(f"兜底策略 {strategy.name} 执行失败: {e}")
                continue
        
        return FallbackResult(
            success=False,
            data=None,
            source="fallback",
            error_message="All fallback strategies failed"
        )
    
    async def _try_fallback_strategy(self, 
                                    strategy: FallbackStrategy,
                                    primary_func: Callable,
                                    *args,
                                    **kwargs) -> FallbackResult:
        """尝试单个兜底策略"""
        start_time = time.time()
        
        try:
            if strategy.fallback_type == FallbackType.CACHE:
                return await self._cache_fallback(strategy, primary_func, *args, **kwargs)
            elif strategy.fallback_type == FallbackType.STATIC:
                return await self._static_fallback(strategy, *args, **kwargs)
            elif strategy.fallback_type == FallbackType.SIMPLIFIED:
                return await self._simplified_fallback(strategy, primary_func, *args, **kwargs)
            elif strategy.fallback_type == FallbackType.QUEUE:
                return await self._queue_fallback(strategy, primary_func, *args, **kwargs)
            elif strategy.fallback_type == FallbackType.RETRY:
                return await self._retry_fallback(strategy, primary_func, *args, **kwargs)
            elif strategy.fallback_type == FallbackType.CUSTOM:
                return await self._custom_fallback(strategy, primary_func, *args, **kwargs)
            else:
                return FallbackResult(
                    success=False,
                    data=None,
                    source="fallback",
                    fallback_type=strategy.fallback_type,
                    error_message=f"Unknown fallback type: {strategy.fallback_type}"
                )
        
        except Exception as e:
            execution_time = time.time() - start_time
            return FallbackResult(
                success=False,
                data=None,
                source="fallback",
                fallback_type=strategy.fallback_type,
                execution_time=execution_time,
                error_message=str(e)
            )
    
    async def _cache_fallback(self, 
                             strategy: FallbackStrategy,
                             primary_func: Callable,
                             *args,
                             **kwargs) -> FallbackResult:
        """缓存兜底"""
        # 生成缓存键
        cache_key = self._generate_cache_key(strategy.name, args, kwargs)
        
        async with self.cache_lock:
            # 检查缓存
            if cache_key in self.fallback_cache:
                data, expiry_time = self.fallback_cache[cache_key]
                if time.time() < expiry_time:
                    if strategy.service_name in self.fallback_stats:
                        self.fallback_stats[strategy.service_name]["cache_hits"] += 1
                    
                    return FallbackResult(
                        success=True,
                        data=data,
                        source="cache",
                        fallback_type=FallbackType.CACHE
                    )
                else:
                    # 缓存过期，删除
                    del self.fallback_cache[cache_key]
        
        # 缓存未命中，尝试执行主要功能并缓存结果
        try:
            if asyncio.iscoroutinefunction(primary_func):
                result = await primary_func(*args, **kwargs)
            else:
                result = await asyncio.get_event_loop().run_in_executor(
                    None, primary_func, *args, **kwargs
                )
            
            # 缓存结果
            ttl = strategy.cache_ttl or self.config.cache_ttl_default
            expiry_time = time.time() + ttl
            
            async with self.cache_lock:
                self.fallback_cache[cache_key] = (result, expiry_time)
                
                # 限制缓存大小
                if len(self.fallback_cache) > self.config.cache_size_limit:
                    # 删除最旧的缓存项
                    oldest_key = min(self.fallback_cache.keys(), 
                                   key=lambda k: self.fallback_cache[k][1])
                    del self.fallback_cache[oldest_key]
            
            return FallbackResult(
                success=True,
                data=result,
                source="fallback",
                fallback_type=FallbackType.CACHE
            )
            
        except Exception as e:
            return FallbackResult(
                success=False,
                data=None,
                source="fallback",
                fallback_type=FallbackType.CACHE,
                error_message=str(e)
            )
    
    async def _static_fallback(self, 
                              strategy: FallbackStrategy,
                              *args,
                              **kwargs) -> FallbackResult:
        """静态内容兜底"""
        static_data = strategy.metadata.get("static_data")
        
        if static_data is not None:
            return FallbackResult(
                success=True,
                data=static_data,
                source="fallback",
                fallback_type=FallbackType.STATIC
            )
        else:
            return FallbackResult(
                success=False,
                data=None,
                source="fallback",
                fallback_type=FallbackType.STATIC,
                error_message="No static data configured"
            )
    
    async def _simplified_fallback(self, 
                                  strategy: FallbackStrategy,
                                  primary_func: Callable,
                                  *args,
                                  **kwargs) -> FallbackResult:
        """简化功能兜底"""
        if strategy.fallback_func:
            try:
                if asyncio.iscoroutinefunction(strategy.fallback_func):
                    result = await strategy.fallback_func(*args, **kwargs)
                else:
                    result = await asyncio.get_event_loop().run_in_executor(
                        None, strategy.fallback_func, *args, **kwargs
                    )
                
                return FallbackResult(
                    success=True,
                    data=result,
                    source="fallback",
                    fallback_type=FallbackType.SIMPLIFIED
                )
            except Exception as e:
                return FallbackResult(
                    success=False,
                    data=None,
                    source="fallback",
                    fallback_type=FallbackType.SIMPLIFIED,
                    error_message=str(e)
                )
        else:
            return FallbackResult(
                success=False,
                data=None,
                source="fallback",
                fallback_type=FallbackType.SIMPLIFIED,
                error_message="No simplified function configured"
            )
    
    async def _queue_fallback(self, 
                             strategy: FallbackStrategy,
                             primary_func: Callable,
                             *args,
                             **kwargs) -> FallbackResult:
        """队列兜底"""
        queue_name = strategy.service_name
        
        # 将请求加入队列
        request_data = {
            "function": primary_func.__name__,
            "args": args,
            "kwargs": kwargs,
            "timestamp": time.time(),
            "strategy": strategy.name
        }
        
        async with self.queue_lock:
            if queue_name not in self.fallback_queues:
                self.fallback_queues[queue_name] = []
            
            # 检查队列大小限制
            if len(self.fallback_queues[queue_name]) >= self.config.queue_size_limit:
                return FallbackResult(
                    success=False,
                    data=None,
                    source="fallback",
                    fallback_type=FallbackType.QUEUE,
                    error_message="Queue is full"
                )
            
            self.fallback_queues[queue_name].append(request_data)
        
        # 返回队列确认
        return FallbackResult(
            success=True,
            data={"message": "Request queued for later processing", "queue_size": len(self.fallback_queues[queue_name])},
            source="fallback",
            fallback_type=FallbackType.QUEUE
        )
    
    async def _retry_fallback(self, 
                             strategy: FallbackStrategy,
                             primary_func: Callable,
                             *args,
                             **kwargs) -> FallbackResult:
        """重试兜底"""
        max_retries = strategy.max_retries or self.config.max_retry_attempts
        retry_delay = strategy.retry_delay or self.config.retry_delay_base
        
        for attempt in range(max_retries):
            try:
                if asyncio.iscoroutinefunction(primary_func):
                    result = await primary_func(*args, **kwargs)
                else:
                    result = await asyncio.get_event_loop().run_in_executor(
                        None, primary_func, *args, **kwargs
                    )
                
                return FallbackResult(
                    success=True,
                    data=result,
                    source="fallback",
                    fallback_type=FallbackType.RETRY,
                    metadata={"attempt": attempt + 1}
                )
                
            except Exception as e:
                if attempt < max_retries - 1:
                    # 指数退避
                    delay = min(retry_delay * (2 ** attempt), self.config.retry_delay_max)
                    await asyncio.sleep(delay)
                else:
                    return FallbackResult(
                        success=False,
                        data=None,
                        source="fallback",
                        fallback_type=FallbackType.RETRY,
                        error_message=f"All retry attempts failed: {e}",
                        metadata={"attempts": max_retries}
                    )
        
        return FallbackResult(
            success=False,
            data=None,
            source="fallback",
            fallback_type=FallbackType.RETRY,
            error_message="Retry fallback failed"
        )
    
    async def _custom_fallback(self, 
                              strategy: FallbackStrategy,
                              primary_func: Callable,
                              *args,
                              **kwargs) -> FallbackResult:
        """自定义兜底"""
        if strategy.fallback_func:
            try:
                if asyncio.iscoroutinefunction(strategy.fallback_func):
                    result = await strategy.fallback_func(*args, **kwargs)
                else:
                    result = await asyncio.get_event_loop().run_in_executor(
                        None, strategy.fallback_func, *args, **kwargs
                    )
                
                return FallbackResult(
                    success=True,
                    data=result,
                    source="fallback",
                    fallback_type=FallbackType.CUSTOM
                )
            except Exception as e:
                return FallbackResult(
                    success=False,
                    data=None,
                    source="fallback",
                    fallback_type=FallbackType.CUSTOM,
                    error_message=str(e)
                )
        else:
            return FallbackResult(
                success=False,
                data=None,
                source="fallback",
                fallback_type=FallbackType.CUSTOM,
                error_message="No custom fallback function configured"
            )
    
    def _generate_cache_key(self, strategy_name: str, args: tuple, kwargs: dict) -> str:
        """生成缓存键"""
        key_data = {
            "strategy": strategy_name,
            "args": args,
            "kwargs": kwargs
        }
        key_string = json.dumps(key_data, sort_keys=True, default=str)
        return hashlib.md5(key_string.encode()).hexdigest()
    
    async def _queue_processor_worker(self) -> None:
        """队列处理工作线程"""
        while self.is_running:
            try:
                await self._process_fallback_queues()
                await asyncio.sleep(self.config.queue_processing_interval)
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"队列处理异常: {e}")
                await asyncio.sleep(5)
    
    async def _process_fallback_queues(self) -> None:
        """处理兜底队列"""
        async with self.queue_lock:
            for queue_name, queue in self.fallback_queues.items():
                if queue:
                    # 处理队列中的请求（这里简化处理）
                    processed_count = min(len(queue), 10)  # 每次最多处理10个
                    for _ in range(processed_count):
                        if queue:
                            request = queue.pop(0)
                            self.logger.info(f"处理队列请求: {queue_name}, {request['function']}")
    
    async def _cache_cleanup_worker(self) -> None:
        """缓存清理工作线程"""
        while self.is_running:
            try:
                await self._cleanup_expired_cache()
                await asyncio.sleep(300)  # 每5分钟清理一次
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"缓存清理异常: {e}")
                await asyncio.sleep(60)
    
    async def _cleanup_expired_cache(self) -> None:
        """清理过期缓存"""
        current_time = time.time()
        expired_keys = []
        
        async with self.cache_lock:
            for key, (data, expiry_time) in self.fallback_cache.items():
                if current_time >= expiry_time:
                    expired_keys.append(key)
            
            for key in expired_keys:
                del self.fallback_cache[key]
        
        if expired_keys:
            self.logger.info(f"清理了 {len(expired_keys)} 个过期缓存项")
    
    async def get_fallback_status(self) -> Dict[str, Any]:
        """获取兜底状态"""
        status = {
            "is_running": self.is_running,
            "strategies": {},
            "cache": {
                "size": len(self.fallback_cache),
                "limit": self.config.cache_size_limit
            },
            "queues": {},
            "stats": self.fallback_stats
        }
        
        # 策略信息
        for service_name, strategies in self.fallback_strategies.items():
            status["strategies"][service_name] = [
                {
                    "name": s.name,
                    "type": s.fallback_type.value,
                    "priority": s.priority.value,
                    "enabled": s.enabled
                }
                for s in strategies
            ]
        
        # 队列信息
        async with self.queue_lock:
            for queue_name, queue in self.fallback_queues.items():
                status["queues"][queue_name] = {
                    "size": len(queue),
                    "limit": self.config.queue_size_limit
                }
        
        return status
    
    async def clear_cache(self, service_name: Optional[str] = None) -> int:
        """清理缓存"""
        async with self.cache_lock:
            if service_name:
                # 清理特定服务的缓存
                keys_to_remove = [
                    key for key in self.fallback_cache.keys()
                    if key.startswith(f"{service_name}_")
                ]
                for key in keys_to_remove:
                    del self.fallback_cache[key]
                return len(keys_to_remove)
            else:
                # 清理所有缓存
                count = len(self.fallback_cache)
                self.fallback_cache.clear()
                return count


# 全局兜底管理器
_global_fallback_manager: Optional[FallbackManager] = None


def get_fallback_manager() -> FallbackManager:
    """获取全局兜底管理器"""
    global _global_fallback_manager
    if _global_fallback_manager is None:
        _global_fallback_manager = FallbackManager()
    return _global_fallback_manager


# 装饰器
def fallback(service_name: str, 
             fallback_type: FallbackType = FallbackType.CACHE,
             priority: FallbackPriority = FallbackPriority.MEDIUM,
             **kwargs):
    """兜底装饰器"""
    def decorator(func):
        async def wrapper(*args, **kwargs_inner):
            manager = get_fallback_manager()
            return await manager.execute_with_fallback(service_name, func, *args, **kwargs_inner)
        return wrapper
    return decorator
